package cmd

import (
	"fmt"
	"gitee.com/zhangyu520cw/restful-api-demo-g7/apps"
	"gitee.com/zhangyu520cw/restful-api-demo-g7/protocol"
	"github.com/infraboard/mcube/logger"
	"github.com/infraboard/mcube/logger/zap"
	"os"
	"os/signal"
	"syscall"

	// 注册所有的服务实例
	_ "gitee.com/zhangyu520cw/restful-api-demo-g7/apps/all"
	"gitee.com/zhangyu520cw/restful-api-demo-g7/conf"
	"github.com/spf13/cobra"
)

var (
	// pusher service config option
	confType string
	confFile string
	confETCD string
)

// 程序的启动和组装都在这里进行
// 1.
// StartCmd represents the base command when called without
var vars bool

var StartCmd = &cobra.Command{
	Use:   "start",
	Short: "启动 demo 后端API",
	Long:  "启动 demo 后端API",
	RunE: func(cmd *cobra.Command, args []string) error {
		// 加载配置文件
		err := conf.LoadConfigFromToml(confFile)
		if err != nil {
			panic(err)
		}
		// 全局日志初始化
		err = loadGlobalLogger()
		if err != nil {
			fmt.Println(err)
		}
		// 加载我们Host Service 的实体类
		// host service 的具体实现
		//service := impl.NewHostServiceImpl()

		// 注册HostService 的实例到IOC
		// 采用： _ "gitee.com/zhangyu520cw/restful-api-demo-g7/apps/host/impl" 完成注册
		//apps.HostService = impl.NewHostServiceImpl()

		// 如何执行hostService的config方法
		// 因为host.HostService 是一个host.Service的接口，并没有包含服务实例初始化（Config）的方法
		apps.InitImpl()
		//api := http.NewHostHTTPHandler()
		//// 从IOC中获取依赖
		//// 提供一个GIN的 Router
		//g := gin.Default()
		//apps.InitGin(g)
		//api.Register(g)
		svc := newManger()
		//return g.Run(conf.C().App.HttpAddr())
		//syscall.SIGTERM
		ch := make(chan os.Signal, 1)
		signal.Notify(ch, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGINT)
		go svc.waitStop(ch)
		return svc.Start()
	},
}

// 问题
//  1. http API, Grpc API 需要启动，消息总线也需要监听，比如负责注册于配置，这些模块都是独立的
//     都需要在启动是，进行启动，都写在start start 膨胀熬不易维护
//  2. 服务的优雅关闭怎么办？外部都会发送一个Terminal（中断信号）给程序，程序是需要处理这个信号的
//     需要实现程序优雅关闭的逻辑处理： 有先后顺序（从外到内完成资源的释放逻辑处理）
//  1. api层的关闭（HTTP,GRPC）
//  2. 消息总线关闭
//  3. 如果使用了注册中心，完成注册中心的注销操作
//  4. 退出完毕
//
// 初始化logger实例

func newManger() *manager {
	return &manager{
		http: protocol.NewHttpService(),
		l:    zap.L().Named("CLI"),
	}
}

type manager struct {
	http *protocol.HttpService
	l    logger.Logger
}

func (m *manager) Start() error {
	return m.http.Start()
}

// 处理来自外部的中断信号，比如Terminal
func (m *manager) waitStop(ch <-chan os.Signal) error {
	for v := range ch {
		switch v {
		//case syscall.SIGTERM:
		//	return m.http.Stop()
		default:
			m.l.Infof("received signal: %s", v)
			return m.http.Stop()
		}
	}
	return nil
}

func loadGlobalLogger() error {
	var (
		logInitMsg string
		level      zap.Level
	)
	// 更加Config里面的日志配置，来配置全局Logger对象
	lc := conf.C().Log
	// 设置日志的级别
	lv, err := zap.NewLevel(lc.Level)
	if err != nil {
		logInitMsg = fmt.Sprintf("%s, use default level INFO", err)
		level = zap.InfoLevel
	} else {
		level = lv
		logInitMsg = fmt.Sprintf("%s, use level %s", logInitMsg, lv.String())
	}
	// 使用默认配置初始化logger的全局配置
	zapConfig := zap.DefaultConfig()

	// 配置日志的level级别
	zapConfig.Level = level

	// 程序每启动一次，不必都生成一个新的日志文件
	zapConfig.Files.RotateOnStartup = false

	// 配置日志的输出方式
	switch lc.To {
	case conf.ToStdout:
		// 把日志打印到标准输出
		zapConfig.ToStderr = true
		// 并没有在日志输入输出到文件
		zapConfig.ToFiles = false
	case conf.ToFile:
		zapConfig.Files.Name = "api.log"
		zapConfig.Files.Path = lc.PathDir
	}
	// 配置日志的输出格式
	switch lc.Format {
	case conf.JsonFormat:
		zapConfig.JSON = true
	}
	if err := zap.Configure(zapConfig); err != nil {
		return err
	}
	zap.L().Named("INIT").Info(logInitMsg)
	return nil
}

func init() {
	StartCmd.PersistentFlags().StringVarP(&confFile, "config", "f", "etc/demo.toml", "指定程序配置文件路径")
	RootCmd.AddCommand(StartCmd)
}
